#include "destor.h"
#include "jcr.h"
#include "utils/sync_queue.h"
#include "index/index.h"
#include "backup.h"

/* defined in index.c */
extern struct {
	/* Requests to the key-value store */
	int lookup_requests;
	int update_requests;
	int lookup_requests_for_unique;
	/* Overheads of prefetching module */
	int read_prefetching_units;
}index_overhead;

extern void close_delta_index();
extern void restore_aware_close();
extern void close_har();

void do_backup(char *path) {

	init_recipe_store();
	init_container_store();
	init_index();

	init_backup_jcr(path); // destor.full_backup_this_version init in this function

    if (jcr.id == 0)
        jcr.container_num_before_backup = 0;
    else
        jcr.container_num_before_backup = get_container_count();

	puts("==== backup begin ====");

	TIMER_DECLARE(1);
	TIMER_BEGIN(1);

	if (destor.simulation_level == SIMULATION_ALL) {
		start_read_trace_phase();
	} else {
		start_read_phase();
		start_chunk_phase();
		start_hash_phase();
	}
	start_dedup_phase();
	start_rewrite_phase();
    start_resemblance_phase();
    start_matching_phase();
	start_delta_phase();
	start_filter_phase();

	if (destor.simulation_level == SIMULATION_ALL) {
		stop_read_trace_phase();
	} else {
		stop_read_phase();
		stop_chunk_phase();
		stop_hash_phase();
	}
	stop_dedup_phase();
	stop_rewrite_phase();
    stop_resemblance_phase();
    stop_matching_phase();
	stop_delta_phase();
	stop_filter_phase(); //close_har() is here, write destor.full_backup_next_version

	TIMER_END(1, jcr.total_time);

    close_har();
    close_delta_index();
    restore_aware_close();
	close_index();
	close_container_store();
	close_recipe_store();

	update_backup_version(jcr.bv);

	free_backup_version(jcr.bv);

    puts("\n");
	puts("==== backup end ====");

	printf("job id: %d\n", jcr.id);
	printf("backup path: %s\n", jcr.path);
    if (destor.full_backup_this_version) {
        printf("this version is full backup\n");
        printf("we %s dedup in dense containers in this full backup\n", 
                        (destor.enable_dedup_in_dense_full_backup == 1) ? "DO allow" : "do NOT allow");

        FILE *excel_full_backup = fopen("excel_full_backup.txt", "a");
        fprintf(excel_full_backup, "full backup version: %d\n", jcr.id + 1);
        fclose(excel_full_backup);
    }
	printf("number of files: %d\n", jcr.file_num);
	printf("number of chunks: %d (%ld bytes on average)\n", jcr.chunk_num,
			jcr.data_size / jcr.chunk_num);
	printf("number of unique chunks: %d\n", jcr.unique_chunk_num);
	printf("number of delta chunks: %d\n", jcr.delta_chunk_num);
	printf("total size(B): %ld\n", jcr.data_size);
	printf("stored data size(B): %ld\n",
			jcr.stored_data_size + jcr.delta_compressed_size);
	printf("stored data size(B) after delta: %ld\n",
			jcr.stored_data_size);
	printf("deduplication ratio: %.4f, %.4f\n",
			jcr.data_size != 0 ?
					(jcr.data_size - jcr.stored_data_size - jcr.delta_compressed_size)
							/ (double) (jcr.data_size) :
					0,
			jcr.data_size
					/ (double) (jcr.stored_data_size + jcr.delta_compressed_size));
	printf("deduplication ratio after delta: %.4f, %.4f\n",
			jcr.data_size != 0 ?
					(jcr.data_size - jcr.stored_data_size)
							/ (double) (jcr.data_size) : 0,
			jcr.data_size
			        / (double) (jcr.stored_data_size));

    printf("unproper delta compression number: %d\n", jcr.unproper_delta_comp_num);
    
    printf("restore speed factor of this backup: %.6f\n", jcr.predict_restore_speed_factor);
    printf("dedup delta chunks: %d\n", jcr.dedup_delta_chunks);
	printf("total time(s): %.3f\n", jcr.total_time / 1000000);
	printf("throughput(MB/s): %.2f\n",
			(double) jcr.data_size * 1000000 / (1024 * 1024 * jcr.total_time));
	printf("number of zero chunks: %d\n", jcr.zero_chunk_num);
    printf("\n");
    printf("number of inherited sparse containers: %d\n", jcr.inherited_sparse_num);
    printf("number of base containers referred: %d\n", jcr.base_container_referred);
    printf("number of base chunk referred: %d\n", jcr.base_chunk_referred);
    printf("container number of the first backup: %d\n", jcr.container_num_of_first_backup);
    printf("number of base chunk from the first backup: %d\n", jcr.base_chunk_num_in_ver1);

    printf("percent of base chunk in ver1: %.3f\n", 1.0 * jcr.base_chunk_num_in_ver1 / jcr.delta_chunk_num);
    printf("\n");
    //printf("number of current sparse container: %d\n", jcr.sparse_container_num);
    //printf("current base container in sparse container: %d\n", jcr.base_container_in_sparse);
    //printf("current base chunk in sparse container: %d\n", jcr.base_chunk_in_sparse_container);
  
    printf("number of containers before: %d\n", jcr.container_num_before_backup);
    printf("number of containers stored: %d\n", jcr.container_num_stored);
    printf("number of containers now: %d\n", jcr.container_num_after_backup);
    
	//printf("size of zero chunks: %ld\n", jcr.zero_chunk_size);
	printf("number of rewritten chunks: %d\n", jcr.rewritten_chunk_num);
    printf("number of rewritten base chunks: %d\n", jcr.rewritten_base_chunk_num);
	printf("size of rewritten chunks: %ld\n", jcr.rewritten_chunk_size);
	printf("rewritten rate in size: %.3f\n",
			jcr.rewritten_chunk_size / (double) jcr.data_size);
    printf("number of freed delta chunks: %lld\n", jcr.freed_delta_number);
    printf("number of chunks has delta: %lld\n", jcr.number_of_chunks_has_delta);
    printf("number of out-of-order/sparse chunks: %lld\n", jcr.number_of_ofc_chunks++);
    printf("duplicate has delta chunks: %d\n", jcr.duplicate_has_delta++);
    printf("nonduplicate has outoforder chunks: %d\n", jcr.noduplicate_has_outoforder);

	destor.data_size += jcr.data_size;
	destor.stored_data_size += jcr.stored_data_size;

	destor.chunk_num += jcr.chunk_num;
	destor.stored_chunk_num += jcr.unique_chunk_num + jcr.rewritten_chunk_num;
	destor.zero_chunk_num += jcr.zero_chunk_num;
	destor.zero_chunk_size += jcr.zero_chunk_size;
	destor.rewritten_chunk_num += jcr.rewritten_chunk_num;
	destor.rewritten_chunk_size += jcr.rewritten_chunk_size;
	destor.total_compressed_size += jcr.total_compressed_size;
	destor.total_size_for_deltacompression += jcr.total_size_for_deltacompression;

	printf("read_time : %.3fs, %.2fMB/s\n", jcr.read_time / 1000000,
			jcr.data_size * 1000000 / jcr.read_time / 1024 / 1024);
	printf("chunk_time : %.3fs, %.2fMB/s\n", jcr.chunk_time / 1000000,
			jcr.data_size * 1000000 / jcr.chunk_time / 1024 / 1024);
	printf("hash_time : %.3fs, %.2fMB/s\n", jcr.hash_time / 1000000,
			jcr.data_size * 1000000 / jcr.hash_time / 1024 / 1024);

	printf("dedup_time : %.3fs, %.2fMB/s\n",
			jcr.dedup_time / 1000000,
			jcr.data_size * 1000000 / jcr.dedup_time / 1024 / 1024);

    printf("sketch_item_number: %lld\n", jcr.sketch_index_item_num);

    printf("resemblance_hash_replace_time : %.3fs, %.2fMB/s\n", jcr.sketch_replace_time / 1000000,
			jcr.data_size * 1000000 / jcr.sketch_replace_time / 1024 / 1024);

	printf("rewrite_time : %.3fs, %.2fMB/s\n", jcr.rewrite_time / 1000000,
			jcr.data_size * 1000000 / jcr.rewrite_time / 1024 / 1024);

	printf("filter_time : %.3fs, %.2fMB/s\n",
			jcr.filter_time / 1000000,
			jcr.data_size * 1000000 / jcr.filter_time / 1024 / 1024);

    printf("super_feature_time : %.3fs, %.2fMB/s\n", jcr.super_feature_time / 1000000,
			jcr.total_size_for_similar_detection * 1000000 / jcr.super_feature_time / 1024 / 1024);

	printf("write_time : %.3fs, %.2fMB/s\n", jcr.write_time / 1000000,
			jcr.data_size * 1000000 / jcr.write_time / 1024 / 1024);

    printf("throughput(MB/s): %.2f\n",
			(double) jcr.data_size * 1000000 / (1024 * 1024 * jcr.total_time));

    printf("\n====================================================================================\n");
    printf("====================================================================================\n");
	//double seek_time = 0.005; //5ms
	//double bandwidth = 120 * 1024 * 1024; //120MB/s

	/*	double index_lookup_throughput = jcr.data_size
	 / (index_read_times * seek_time
	 + index_read_entry_counter * 24 / bandwidth) / 1024 / 1024;

	 double write_data_throughput = 1.0 * jcr.data_size * bandwidth
	 / (jcr->unique_chunk_num) / 1024 / 1024;
	 double index_read_throughput = 1.0 * jcr.data_size / 1024 / 1024
	 / (index_read_times * seek_time
	 + index_read_entry_counter * 24 / bandwidth);
	 double index_write_throughput = 1.0 * jcr.data_size / 1024 / 1024
	 / (index_write_times * seek_time
	 + index_write_entry_counter * 24 / bandwidth);*/

	/*	double estimated_throughput = write_data_throughput;
	 if (estimated_throughput > index_read_throughput)
	 estimated_throughput = index_read_throughput;*/
	/*if (estimated_throughput > index_write_throughput)
	 estimated_throughput = index_write_throughput;*/

	char logfile[] = "backup.log";
	FILE *fp = fopen(logfile, "a");
	/*
	 * job id,
	 * the size of backup
	 * accumulative consumed capacity,
	 * deduplication rate,
	 * rewritten rate,
	 * total container number referred,
	 * sparse container number,
	 * inherited container number,
	 * 4 * index overhead (4 * int)
	 * throughput,
	 */
	fprintf(fp, "%d %ld %ld %.4f %.4f %d %d %d %d %d %d %d %.2f\n",
			jcr.id,
			jcr.data_size,
			destor.stored_data_size,
			jcr.data_size != 0 ?
					(jcr.data_size - jcr.rewritten_chunk_size - jcr.delta_data_size)/(double) (jcr.data_size)
					: 0,
			jcr.data_size != 0 ? (double) (jcr.rewritten_chunk_size) / (double) (jcr.data_size) : 0,
			jcr.total_container_num_referred,
			jcr.sparse_container_num,
			jcr.inherited_sparse_num,
			index_overhead.lookup_requests,
			index_overhead.lookup_requests_for_unique,
			index_overhead.update_requests,
			index_overhead.read_prefetching_units,
			(double) jcr.data_size * 1000000 / (1024 * 1024 * jcr.total_time));

	fclose(fp);

    FILE *excel_backup = fopen("excel_backup.txt", "a");
    fprintf(excel_backup, "%d\t%d\t%ld\t%d\n", jcr.id + 1,
            jcr.chunk_num,
            jcr.data_size / jcr.chunk_num,
            jcr.container_num_after_backup);
    fclose(excel_backup);

	/*
      FILE *excel_dedup = fopen("excel_dedup.txt", "a");
      fprintf(excel_dedup, "%d\t%.4f\t%.4f\t%.4f\t%.4f\n", jcr.id + 1, 
          jcr.data_size != 0 ? (jcr.data_size - jcr.unique_data_size - jcr.rewritten_chunk_size)
							/ (double) (jcr.data_size) : 0,
			jcr.data_size / (double) (jcr.unique_data_size + jcr.rewritten_chunk_size),
            jcr.data_size != 0 ? (jcr.data_size - jcr.delta_data_size - jcr.rewritten_base_chunk_size)
							/ (double) (jcr.data_size) : 0,
			jcr.data_size / (double) (jcr.delta_data_size + jcr.rewritten_base_chunk_size));
      fclose(excel_dedup);
    */
    FILE *excel_dedup = fopen("excel_dedup.txt", "a");
      fprintf(excel_dedup, "%d\t%.4f\t%.4f\t%.4f\t%.4f\n", jcr.id + 1, 
          jcr.data_size != 0 ? (jcr.data_size - jcr.unique_data_size - jcr.rewritten_chunk_size)
							/ (double) (jcr.data_size) : 0,
			jcr.data_size / (double) (jcr.unique_data_size + jcr.rewritten_chunk_size),
            jcr.data_size != 0 ? (jcr.data_size - jcr.stored_data_size)
							/ (double) (jcr.data_size) : 0,
			jcr.data_size / (double) (jcr.stored_data_size));
      fclose(excel_dedup);

    FILE *excel_basecache = fopen("excel_basecache.txt", "a");
    fprintf(excel_basecache, "%d\t%d\t%d\t%d\n", jcr.id + 1, jcr.base_cache_miss, jcr.base_cache_hit, jcr.base_container_read);
    fclose(excel_basecache);

    FILE *excel_predict_restore = fopen("excel_predict_restore.txt", "a");
    fprintf(excel_predict_restore, "%d\t%.6f\n", jcr.id + 1, jcr.id + 1, jcr.predict_restore_speed_factor);
    fclose(excel_predict_restore);

    FILE *excel_sf_number = fopen("excel_sf_number.txt", "a");
    fprintf(excel_sf_number, "%d\t%lld\n", jcr.id + 1, jcr.total_sf_num);
    fclose(excel_sf_number);
    
    FILE *excel_delta_times = fopen("excel_deltaCompression_number.txt", "a");
    fprintf(excel_delta_times, "%d\t%lld\n", jcr.id + 1, (jcr.dedup_delta_chunks + jcr.delta_compression_times));
    fclose(excel_delta_times);

    FILE *excel_throughput = fopen("excel_throughput.txt", "a");
    fprintf(excel_throughput, "%d\t%.2fMB/s\t%.2fMB/s\t%.2fMB/s\n", jcr.id + 1,
        jcr.data_size * 1000000 / jcr.chunk_time / 1024 / 1024,
        jcr.data_size * 1000000 / jcr.super_feature_time / 1024 / 1024,
        (double) jcr.data_size * 1000000 / (1024 * 1024 * jcr.total_time));
    fclose(excel_throughput);

    FILE *excel_slack_content = fopen("excel_slack_content.txt", "a");
    fprintf(excel_slack_content, "%d\t%.4f\n", jcr.id + 1, 1.0*jcr.slack_content/jcr.total_size_for_similar_detection);
    fclose(excel_slack_content);

    FILE *excel_delta_efficiency = fopen("excel_delta_efficiency.txt", "a");
    fprintf(excel_delta_efficiency, "%d\t%.6f\n", jcr.id + 1, 1.0*jcr.delta_compressed_size/jcr.size_before_compressed);
    fclose(excel_delta_efficiency);

    FILE *excel_average_compressed_size = fopen("excel_average_compressed_size.txt", "a");
    fprintf(excel_average_compressed_size, "%d\t%.2f\n", jcr.id + 1, 1.0*jcr.delta_compressed_size/jcr.delta_compression_times);
    fclose(excel_average_compressed_size);

	FILE *excel_matched_sf_statics = fopen("excel_matched_sf_statics.txt", "a");
    fprintf(excel_matched_sf_statics, "%d\t%d\t%d\t%d\n", 
		jcr.id + 1, jcr.sf_matched_number, jcr.multi_matched_chunk_number, jcr.multi_matched_diff_number);
    fclose(excel_matched_sf_statics);
}
